package networkLoader;

import network.Device;
import network.Network;
import network.Rule;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

public class FBNetwork {
    private static final int npod = 112;

    private static Network buildTopology() {
        Network n = new Network("FBNetwork");
        //// create devices
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int j = 0; j < 48; j++) {
                Device device = n.addDevice("rsw-" + iPod + "-" + j);
                // add rsw -> host port
                device.addPort(String.format("rsw-%d-%d>h-%d-%d", iPod, j, iPod, j));
            }

            for (int j = 0; j < 4; j++) {
                n.addDevice("fsw-" + iPod + "-" + j);
            }
        }
        for (int iSpine = 0; iSpine < 4; iSpine++) {
            for (int j = 0; j < 48; j++) {
                n.addDevice("ssw-" + iSpine + "-" + j);
            }
        }
        //// \create devices

        //// create links
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int iFsw = 0; iFsw < 4; iFsw++) {
                String fsw = "fsw-" + iPod + "-" + iFsw;
                // down links
                for (int iRsw = 0; iRsw < 48; iRsw++) {
                    String rsw = "rsw-" + iPod + "-" + iRsw;
                    n.addLink(fsw, fsw + ">" + rsw, rsw, rsw + ">" + fsw);
                }
                // up links
                for (int iSsw = 0; iSsw < 48; iSsw++) {
                    String ssw = "ssw-" + iFsw + "-" + iSsw;
                    n.addLink(fsw, fsw + ">" + ssw, ssw, ssw + ">" + fsw);
                }
            }
        }
        //// \create links
        for (Device device : n.getAllDevices()) device.uid = Device.cnt++;
        return n;
    }

    public static Network getNetwork() {
        Network n = buildTopology();

        //// create rules
        // first 8 bits is pod id, next 8 bit is index of rsw in pod
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int iRsw = 0; iRsw < 48; iRsw++) {
                String rsw = "rsw-" + iPod + "-" + iRsw;
                Device device = n.getDevice(rsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (dstrsw.equals(rsw)) {
                            rule = new Rule(device, dstip, 16, device.getPort(rsw + ">h-" + jPod + "-" + jRsw));
                        } else {
                            String dstfsw = "fsw-" + iPod + "-" + (48 * jPod + jRsw) % 4;
                            rule = new Rule(device, dstip, 16, device.getPort(rsw + ">" + dstfsw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }

            for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
                String fsw = "fsw-" + iPod + "-" + iSpine;
                Device device = n.getDevice(fsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (jPod == iPod) { // intra pod
                            rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstrsw));
                        } else {
                            String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw) % 48;
                            rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstssw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
            for (int iSsw = 0; iSsw < 48; iSsw++) {
                String ssw = "ssw-" + iSpine + "-" + iSsw;
                Device device = n.getDevice(ssw);
                for (int k = 0; k < npod; k++) {
                    for (int l = 0; l < 48; l++) {
                        long dstip = (k << 24) + ((l + 1) << 16);
                        String dstfsw = "fsw-" + k + "-" + iSpine;
                        Rule rule = new Rule(device, dstip, 16, device.getPort(ssw + ">" + dstfsw));
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        //// \create rules

        return n;
    }

    /**
     * NOTE: the ip prefix format is podId.rswId.0.0/16, podId start from 1, since podId=0 lead to overlap with default rule
     * The aggr prefix enlarge the origin match space, which lead new ecs extracted from default rule
     *
     * @return
     */
    public static Network getNetworkWithAggrRules() {
        Network n = buildTopology();
        boolean aggrRsw = false;
        //// create rules
        // first 8 bits is pod id, next 8 bit is index of rsw in pod
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int iRsw = 0; iRsw < 48; iRsw++) {
                String rsw = "rsw-" + iPod + "-" + iRsw;
                Device device = n.getDevice(rsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    if (!aggrRsw) {
                        for (int jRsw = 0; jRsw < 48; jRsw++) {
                            String dstrsw = "rsw-" + jPod + "-" + jRsw;
                            long dstip = ((jPod + 1) << 24) + ((jRsw + 1) << 16);
                            Rule rule;
                            if (dstrsw.equals(rsw)) {
                                rule = new Rule(device, dstip, 16, device.getPort(rsw + ">h-" + jPod + "-" + jRsw));
                            } else {
                                String dstfsw = "fsw-" + iPod + "-" + (48 * jPod + jRsw) % 4;
                                rule = new Rule(device, dstip, 16, device.getPort(rsw + ">" + dstfsw));
                            }
                            n.addInitialRule(rule);
                            device.addInitialRule(rule);
                        }
                    } else {
                        if (iPod == jPod) { // intra pod
                            for (int jRsw = 0; jRsw < 48; jRsw++) {
                                String dstrsw = "rsw-" + jPod + "-" + jRsw;
                                long dstip = ((jPod + 1) << 24) + ((jRsw + 1) << 16);
                                Rule rule;
                                if (dstrsw.equals(rsw)) {
                                    rule = new Rule(device, dstip, 16, device.getPort(rsw + ">h-" + jPod + "-" + jRsw));
                                } else {
                                    String dstfsw = "fsw-" + iPod + "-" + jRsw % 4;
                                    rule = new Rule(device, dstip, 16, device.getPort(rsw + ">" + dstfsw));
                                }
                                n.addInitialRule(rule);
                                device.addInitialRule(rule);
                            }
                        } else {
                            String dstfsw = "fsw-" + iPod + "-" + jPod % 4;
                            long dstip = ((jPod + 1) << 24);
                            Rule rule = new Rule(device, dstip, 8, device.getPort(rsw + ">" + dstfsw));
                            n.addInitialRule(rule);
                            device.addInitialRule(rule);
                        }
                    }
                }
            }

            for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
                String fsw = "fsw-" + iPod + "-" + iSpine;
                Device device = n.getDevice(fsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    if (jPod == iPod) { // intra pod
                        for (int jRsw = 0; jRsw < 48; jRsw++) {
                            String dstrsw = "rsw-" + jPod + "-" + jRsw;
                            long dstip = ((jPod + 1) << 24) + ((jRsw + 1) << 16);
                            Rule rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstrsw));
                            n.addInitialRule(rule);
                            device.addInitialRule(rule);
                        }
                    } else { // inter pod
                        String dstssw = "ssw-" + iSpine + "-" + jPod % 48;
                        long dstip = ((jPod + 1) << 24);
                        Rule rule = new Rule(device, dstip, 8, device.getPort(fsw + ">" + dstssw));
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }

                }
            }
        }
        for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
            for (int iSsw = 0; iSsw < 48; iSsw++) {
                String ssw = "ssw-" + iSpine + "-" + iSsw;
                Device device = n.getDevice(ssw);
                for (int k = 0; k < npod; k++) {
                    long dstip = ((k + 1) << 24);
                    String dstfsw = "fsw-" + k + "-" + iSpine;
                    Rule rule = new Rule(device, dstip, 8, device.getPort(ssw + ">" + dstfsw));
                    n.addInitialRule(rule);
                    device.addInitialRule(rule);
                }
            }
        }
        //// \create rules

        return n;
    }

    /**
     * A hacking data set using x<<8 as src id
     *
     * @return
     */
    public static Network getNetworkSrcHack() {
        Network n = buildTopology();

        double prop = 0.1;
        double x = 1.0001;
        //// create rules
        // first 8 bits is pod id, next 8 bit is index of rsw in pod
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int iRsw = 0; iRsw < 48; iRsw++) {
                String rsw = "rsw-" + iPod + "-" + iRsw;
                Device device = n.getDevice(rsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
//                        double pro = 1 - n * n;
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (dstrsw.equals(rsw)) {
                            rule = new Rule(device, dstip, 16, device.getPort(rsw + ">h-" + jPod + "-" + jRsw));
                        } else {
                            String dstfsw = "fsw-" + iPod + "-" + (48 * jPod + jRsw) % 4;
                            rule = new Rule(device, dstip, 16, device.getPort(rsw + ">" + dstfsw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }

            for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
                String fsw = "fsw-" + iPod + "-" + iSpine;
                Device device = n.getDevice(fsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (jPod == iPod) { // intra pod
                            rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstrsw));
                            n.addInitialRule(rule);
                            device.addInitialRule(rule);
                        } else {
                            if (iPod == 0 && (jPod < 5)) {
//                                if (Math.random() < prop) {
                                for (int i = 0; i < 48; i++) {
                                    long srcdstip = dstip + (i << 8);
//                                    String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw + i) % 48;
                                    String dstssw = "ssw-" + iSpine + "-" + (i - 1);
//                                String dstssw = "ssw-" + iSpine + "-" + (i+iSpine) % 48;
                                    rule = new Rule(device, srcdstip, 24, device.getPort(fsw + ">" + dstssw));
                                    n.addInitialRule(rule);
                                    device.addInitialRule(rule);
                                }
                            } else {
                                String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw) % 48;
                                rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstssw));
                                n.addInitialRule(rule);
                                device.addInitialRule(rule);
                            }
                            prop /= x;
                            x *= x;
                        }
                    }
                }
            }
        }
        for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
            for (int iSsw = 0; iSsw < 48; iSsw++) {
                String ssw = "ssw-" + iSpine + "-" + iSsw;
                Device device = n.getDevice(ssw);
                for (int k = 0; k < npod; k++) {
                    for (int l = 0; l < 48; l++) {
                        long dstip = (k << 24) + ((l + 1) << 16);
                        String dstfsw = "fsw-" + k + "-" + iSpine;
                        Rule rule = new Rule(device, dstip, 16, device.getPort(ssw + ">" + dstfsw));
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        //// \create rules

        return n;
    }

    public static Network getNetworkSrcHackMore() {
        Network n = buildTopology();

        //// create rules
        // first 8 bits is pod id, next 8 bit is index of rsw in pod
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int iRsw = 0; iRsw < 48; iRsw++) {
                String rsw = "rsw-" + iPod + "-" + iRsw;
                Device device = n.getDevice(rsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (dstrsw.equals(rsw)) {
                            rule = new Rule(device, dstip, 16, device.getPort(rsw + ">h-" + jPod + "-" + jRsw));
                        } else {
                            String dstfsw = "fsw-" + iPod + "-" + (48 * jPod + jRsw) % 4;
                            rule = new Rule(device, dstip, 16, device.getPort(rsw + ">" + dstfsw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }

            for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
                String fsw = "fsw-" + iPod + "-" + iSpine;
                Device device = n.getDevice(fsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (jPod == iPod) { // intra pod
                            rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstrsw));
                            n.addInitialRule(rule);
                            device.addInitialRule(rule);
                        } else {
                            if ((jPod < 5)) {
//                                if (Math.random() < prop) {
                                for (int i = 0; i < 32; i++) {
                                    long srcdstip = dstip + (i << 8);
//                                    String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw + i) % 48;
                                    String dstssw = "ssw-" + iSpine + "-" + (i);
//                                String dstssw = "ssw-" + iSpine + "-" + (i+iSpine) % 48;
                                    rule = new Rule(device, srcdstip, 24, device.getPort(fsw + ">" + dstssw));
                                    n.addInitialRule(rule);
                                    device.addInitialRule(rule);
                                }
                            } else {
                                String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw) % 48;
                                rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstssw));
                                n.addInitialRule(rule);
                                device.addInitialRule(rule);
                            }
                        }
                    }
                }
            }
        }
        for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
            for (int iSsw = 0; iSsw < 48; iSsw++) {
                String ssw = "ssw-" + iSpine + "-" + iSsw;
                Device device = n.getDevice(ssw);
                for (int k = 0; k < npod; k++) {
                    for (int l = 0; l < 48; l++) {
                        long dstip = (k << 24) + ((l + 1) << 16);
                        String dstfsw = "fsw-" + k + "-" + iSpine;
                        Rule rule = new Rule(device, dstip, 16, device.getPort(ssw + ">" + dstfsw));
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        //// \create rules

        return n;
    }

    public static int log2nlz(int bits) {
        if (bits == 0)
            return 0; // or throw exception
        return 31 - Integer.numberOfLeadingZeros(bits);
    }

    public static Network getNetworkUnbalanceSubspace() {
        Network n = buildTopology();

        //// create rules
        // first 8 bits is pod id, next 8 bit is index of rsw in pod
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int iRsw = 0; iRsw < 48; iRsw++) {
                String rsw = "rsw-" + iPod + "-" + iRsw;
                Device device = n.getDevice(rsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (log2nlz(jPod * 48 + jRsw + 1) << 24) + ((jPod * 48 + jRsw + 1) << 8);//(jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (dstrsw.equals(rsw)) {
                            rule = new Rule(device, dstip, 24, device.getPort(rsw + ">h-" + jPod + "-" + jRsw));
                        } else {
                            String dstfsw = "fsw-" + iPod + "-" + (48 * jPod + jRsw) % 4;
                            rule = new Rule(device, dstip, 24, device.getPort(rsw + ">" + dstfsw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }

            for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
                String fsw = "fsw-" + iPod + "-" + iSpine;
                Device device = n.getDevice(fsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (log2nlz(jPod * 48 + jRsw + 1) << 24) + ((jPod * 48 + jRsw + 1) << 8);//(jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (jPod == iPod) { // intra pod
                            rule = new Rule(device, dstip, 24, device.getPort(fsw + ">" + dstrsw));
                        } else {
                            String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw) % 48;
                            rule = new Rule(device, dstip, 24, device.getPort(fsw + ">" + dstssw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
            for (int iSsw = 0; iSsw < 48; iSsw++) {
                String ssw = "ssw-" + iSpine + "-" + iSsw;
                Device device = n.getDevice(ssw);
                for (int k = 0; k < npod; k++) {
                    for (int l = 0; l < 48; l++) {
                        long dstip = (log2nlz(k * 48 + l + 1) << 24) + ((k * 48 + l + 1) << 8);//(k << 24) + ((l + 1) << 16);
                        String dstfsw = "fsw-" + k + "-" + iSpine;
                        Rule rule = new Rule(device, dstip, 24, device.getPort(ssw + ">" + dstfsw));
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        //// \create rules

        return n;
    }

    public static Network getNetworkForDeltanet() {
        Network n = buildTopology();

        //// create rules
        // first 8 bits is pod id, next 8 bit is index of rsw in pod
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int iRsw = 0; iRsw < 48; iRsw++) {
                String rsw = "rsw-" + iPod + "-" + iRsw;
                Device device = n.getDevice(rsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
//                        double pro = 1 - n * n;
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (dstrsw.equals(rsw)) {
                            rule = new Rule(device, dstip, 24, device.getPort(rsw + ">h-" + jPod + "-" + jRsw));
                        } else {
                            String dstfsw = "fsw-" + iPod + "-" + (48 * jPod + jRsw) % 4;
                            rule = new Rule(device, dstip, 24, device.getPort(rsw + ">" + dstfsw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }

            for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
                String fsw = "fsw-" + iPod + "-" + iSpine;
                Device device = n.getDevice(fsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (jPod == iPod) { // intra pod
                            rule = new Rule(device, dstip, 24, device.getPort(fsw + ">" + dstrsw));
                            n.addInitialRule(rule);
                            device.addInitialRule(rule);
                        } else {
                            if ((jPod < 5)) {
//                                if (Math.random() < prop) {
                                for (int i = 0; i < npod; i++) {
//                                    String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw + i) % 48;
                                    // String dstssw = "ssw-" + iSpine + "-" + (i - 1);
                                    String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw) % 48;
//                                String dstssw = "ssw-" + iSpine + "-" + (i+iSpine) % 48;
                                    rule = new Rule(device, i, 8, dstip, 24, device.getPort(fsw + ">" + dstssw));
                                    rule.setPriority(31);
                                    n.addInitialRule(rule);
                                    device.addInitialRule(rule);
                                }
                            }
                            String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw) % 48;
                            rule = new Rule(device, dstip, 24, device.getPort(fsw + ">" + dstssw));
                            n.addInitialRule(rule);
                            device.addInitialRule(rule);
                        }
                    }
                }
            }
        }
        for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
            for (int iSsw = 0; iSsw < 48; iSsw++) {
                String ssw = "ssw-" + iSpine + "-" + iSsw;
                Device device = n.getDevice(ssw);
                for (int k = 0; k < npod; k++) {
                    for (int l = 0; l < 48; l++) {
                        long dstip = (k << 24) + ((l + 1) << 16);
                        String dstfsw = "fsw-" + k + "-" + iSpine;
                        Rule rule = new Rule(device, dstip, 24, device.getPort(ssw + ">" + dstfsw));
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }

        return n;
    }

    public static Network getNetworkUnAllPair() {
        Network n = buildTopology();

        //// create rules
        // first 8 bits is pod id, next 8 bit is index of rsw in pod
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int iRsw = 0; iRsw < 48; iRsw++) {
                /// for error
                if (iPod == 1 && iRsw == 0) continue;
                String rsw = "rsw-" + iPod + "-" + iRsw;
                Device device = n.getDevice(rsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (dstrsw.equals(rsw)) {
                            rule = new Rule(device, dstip, 16, device.getPort(rsw + ">h-" + jPod + "-" + jRsw));
                        } else {
                            String dstfsw = "fsw-" + iPod + "-" + (48 * jPod + jRsw) % 4;
                            rule = new Rule(device, dstip, 16, device.getPort(rsw + ">" + dstfsw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }

            for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
                String fsw = "fsw-" + iPod + "-" + iSpine;
                Device device = n.getDevice(fsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (jPod == iPod) { // intra pod
                            rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstrsw));
                        } else {
                            String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw) % 48;
                            rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstssw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
            for (int iSsw = 0; iSsw < 48; iSsw++) {
                String ssw = "ssw-" + iSpine + "-" + iSsw;
                Device device = n.getDevice(ssw);
                for (int k = 0; k < npod; k++) {
                    for (int l = 0; l < 48; l++) {
                        long dstip = (k << 24) + ((l + 1) << 16);
                        String dstfsw = "fsw-" + k + "-" + iSpine;
                        Rule rule = new Rule(device, dstip, 16, device.getPort(ssw + ">" + dstfsw));
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        //// \create rules

        return n;
    }

    public static void dump(Network network, String filename) {
        try {
            FileWriter writer = new FileWriter(filename, false);
            BufferedWriter bufferedWriter = new BufferedWriter(writer);
            for (Rule rule : network.getInitialRules()) {
                bufferedWriter.write(rule.getDevice().getName() + " " + rule.getMatch().longValue() + " " + rule.getPrefix() + " " + rule.getOutPort().getName());
                bufferedWriter.newLine();
            }
            bufferedWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Network load(String filename) {
        Network network = buildTopology();
        try {
            FileReader reader = new FileReader(filename);
            BufferedReader bufferedReader = new BufferedReader(reader);
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                String[] arr = line.split(" ");
                Device device = network.getDevice(arr[0]);
                Rule rule = new Rule(device, Long.parseLong(arr[1]), Integer.parseInt(arr[2]), device.getPort(arr[3]));
                device.addInitialRule(rule);
                network.addInitialRule(rule);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return network;
    }

    public static Network getErrorNetwork(long errorIp) {
        Network n = buildTopology();

        //// create rules
        // first 8 bits is pod id, next 8 bit is index of rsw in pod
        for (int iPod = 0; iPod < npod; iPod++) {
            for (int iRsw = 0; iRsw < 48; iRsw++) {
                String rsw = "rsw-" + iPod + "-" + iRsw;
                Device device = n.getDevice(rsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (dstrsw.equals(rsw)) {
                            if (dstip == errorIp)
                                rule = new Rule(device, dstip, 16, device.getPort("default"));
                            else
                                rule = new Rule(device, dstip, 16, device.getPort(rsw + ">h-" + jPod + "-" + jRsw));
                        } else {
                            String dstfsw = "fsw-" + iPod + "-" + (48 * jPod + jRsw) % 4;
                            if (dstip == errorIp)
                                rule = new Rule(device, dstip, 16, device.getPort("default"));
                            else
                                rule = new Rule(device, dstip, 16, device.getPort(rsw + ">" + dstfsw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }

            for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
                String fsw = "fsw-" + iPod + "-" + iSpine;
                Device device = n.getDevice(fsw);
                for (int jPod = 0; jPod < npod; jPod++) {
                    for (int jRsw = 0; jRsw < 48; jRsw++) {
                        String dstrsw = "rsw-" + jPod + "-" + jRsw;
                        long dstip = (jPod << 24) + ((jRsw + 1) << 16);
                        Rule rule;
                        if (jPod == iPod) { // intra pod
                            if (dstip == errorIp)
                                rule = new Rule(device, dstip, 16, device.getPort("default"));
                            else
                                rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstrsw));
                        } else {
                            String dstssw = "ssw-" + iSpine + "-" + (48 * jPod + jRsw) % 48;
                            if (dstip == errorIp)
                                rule = new Rule(device, dstip, 16, device.getPort("default"));
                            else
                                rule = new Rule(device, dstip, 16, device.getPort(fsw + ">" + dstssw));
                        }
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        for (int iSpine = 0; iSpine < 4; iSpine++) { // spine id
            for (int iSsw = 0; iSsw < 48; iSsw++) {
                String ssw = "ssw-" + iSpine + "-" + iSsw;
                Device device = n.getDevice(ssw);
                for (int k = 0; k < npod; k++) {
                    for (int l = 0; l < 48; l++) {
                        long dstip = (k << 24) + ((l + 1) << 16);
                        String dstfsw = "fsw-" + k + "-" + iSpine;
                        Rule rule;
                        if (dstip == errorIp)
                            rule = new Rule(device, dstip, 16, device.getPort("default"));
                        else
                            rule = new Rule(device, dstip, 16, device.getPort(ssw + ">" + dstfsw));
                        n.addInitialRule(rule);
                        device.addInitialRule(rule);
                    }
                }
            }
        }
        //// \create rules

        return n;
    }

    public static HashMap<String, Network> getPartitions(Network network, int nPartition) {
        HashMap<String, Network> nameToPartition = new HashMap<>();
        int nRswPerPartition = 112 * 48 / nPartition;
        int nFswPerPartition = 112 * 4 / nPartition;
        Device[][] partitionDevices = new Device[nPartition][112 * 52 / nPartition];
        for (int iPod = 0; iPod < 112; iPod++) {
            for (int iRsw = 0; iRsw < 48; iRsw++) {
                partitionDevices[(iPod * 48 + iRsw) / nRswPerPartition][(iPod * 48 + iRsw) % nRswPerPartition] = network.getDevice("rsw-" + iPod + "-" + iRsw);
            }
            for (int iFsw = 0; iFsw < 4; iFsw++) {
                partitionDevices[(iPod * 4 + iFsw) / nFswPerPartition][nRswPerPartition + (iPod * 4 + iFsw) % nFswPerPartition] = network.getDevice("fsw-" + iPod + "-" + iFsw);
            }
        }
        for (int iPartition = 0; iPartition < nPartition; iPartition++) {
            Network partition = network.getSubNetwork(Arrays.asList(partitionDevices[iPartition]));
            nameToPartition.put("p-" + iPartition, partition);
        }
        for (int i = 0; i < 4; i++) { // spine
            LinkedList<Device> partition = new LinkedList<>();
            for (int j = 0; j < 48; j++) { // spine.ssw
                String ssw = "ssw-" + i + "-" + j;
                partition.add(network.getDevice(ssw));
            }
            Network pt = network.getSubNetwork(partition);
            nameToPartition.put("spine-" + i, pt);
        }

        return nameToPartition;
    }

    public static void main(String[] args) {
//        System.out.println(Math.random());System.exit(1);
        Network network = getNetworkSrcHackMore();
//        Network network = load("fbrules");
        List<Integer> nRules = network.getAllDevices().stream().filter(device -> device.getName().startsWith("f")).map(device -> device.getInitialRules().size()).collect(Collectors.toList());
        System.out.println(Collections.max(nRules));
        System.out.println(Collections.min(nRules));
        System.out.println(network.getInitialRules().size());
//        dump(network, "fbrules");
    }
}
